﻿

namespace SharpSoft
{
    using SharpSoft.DAOS;
    using SharpSoft.Data;
    using SharpSoft.Models;
    /// <summary>
    /// 任务调度器，从数据库取出任务并执行
    /// </summary>
    public class TaskScheduler : IDisposable
    {

        private readonly Database db;
        private readonly System.Threading.Timer timer;
        private Action<TaskModel>? executeMethod;
        private readonly List<Thread> threads;
        private readonly object asyncState_threads = new object();
        public TaskScheduler(Database p_db)
        {
            this.db = p_db;
            threads = new List<Thread>();
            timer = new Timer(new TimerCallback(this.check));
        }

        /// <summary>
        /// 检查任务的时间间隔
        /// </summary>
        public TimeSpan CheckInterval { get; set; } = TimeSpan.FromSeconds(5);
        /// <summary>
        /// 任务的有效期（超过这个时间未执行完的任务会被清理）
        /// </summary>
        public TimeSpan ExpiredSpan { get; set; } = TimeSpan.FromDays(30);

        /// <summary>
        /// 最大线程数量
        /// </summary>
        public int MaxThreadCount { get; set; } = 3;
        /// <summary>
        /// 每次检查获取的任务数量
        /// </summary>
        public int CheckCount { get; set; } = 10;
        /// <summary>
        /// 若指定一个值则表示此调度器只处理具有该类型标识的任务。为null表示处理所有类型的任务。
        /// </summary>
        public string? TaskTypeFlag { get; set; } = null;

        #region 任务处理流程

        /// <summary>
        /// 检查新的任务
        /// </summary>
        /// <param name="state"></param>
        private void check(object state)
        {
            int ncount = 0;
            lock (asyncState_threads)
            {
                ncount = Math.Min(CheckCount, MaxThreadCount - threads.Count);
                if (ncount == 0)
                {//线程已满
                    return;
                }

            }
            var tasks = GetTasksForExecting(ncount);
            foreach (var task in tasks)
            {
                beginTask(task);
            }
        }
        /// <summary>
        /// 开始任务
        /// </summary>
        /// <param name="taskModel"></param>
        private void beginTask(TaskModel taskModel)
        {
            Thread thread = new Thread(new ParameterizedThreadStart(threadInvoke));
            lock (asyncState_threads)
            {
                threads.Add(thread);
            }
            thread.Start(taskModel);
        }

        /// <summary>
        /// 线程函数
        /// </summary>
        /// <param name="para"></param>
        private void threadInvoke(object para)
        {
            var task = (TaskModel)para;
            var taskId = task.Id;
            try
            {
                try
                {
                    using (var dbt = this.db.Clone())
                    {//标记任务的开始时间
                        var dao = new TaskDAO(dbt);
                        dao.SetStartTime(task.Id);
                    }
                }
                catch (Exception)
                {
                    throw;
                }

                executeMethod?.Invoke(task);
                try
                {
                    using (var dbt = this.db.Clone())
                    { //标记任务的结束时间和完成状态
                        var dao = new TaskDAO(dbt);
                        dao.SetStopTime(taskId, TaskState.Executed);
                    }
                }
                catch (Exception)
                {
                }

            }
            catch (Exception)
            {
                try
                {
                    using (var dbt = this.db.Clone())
                    {//标记任务的结束时间和失败状态
                        var dao = new TaskDAO(dbt);
                        dao.SetStopTime(taskId, TaskState.Failed);
                    }
                }
                catch (Exception)
                {
                }
            }
            finally
            {//空出当前线程
                removeThread(Thread.CurrentThread);
            }

        }
        /// <summary>
        /// 移除已执行完毕的线程
        /// </summary>
        /// <param name="thread"></param>
        private void removeThread(Thread thread)
        {
            lock (asyncState_threads)
            {
                threads.Remove(thread);
            }

        }
        #endregion


        #region public


        /// <summary>
        /// 开始检查并执行任务
        /// </summary>
        public void Start(Action<TaskModel> method)
        {
            using (var dbt = this.db.Clone())
            {//清理过期未执行完的任务
                var dao = new TaskDAO(dbt);
                dao.DeleteExpiredTasks();
                dao.DeleteExpiredTasks(this.ExpiredSpan);
            }
            this.executeMethod = method;
            timer.Change(this.CheckInterval, this.CheckInterval);
        }

        /// <summary>
        /// 获取一批任务准备执行
        /// </summary>
        /// <param name="count"></param>
        /// <returns></returns>
        private TaskModel[] GetTasksForExecting(int count)
        {
            using (var dbt = this.db.Clone())
            {
                var dao = new TaskDAO(dbt);
                using (var tran = dbt.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
                {
                    try
                    {
                        var tasks = dao.GetFreeTasks(count, this.TaskTypeFlag);
                        List<long> ids = new List<long>();
                        foreach (var item in tasks)
                        {
                            ids.Add(item.Id);
                        }
                        dao.UpdateState(ids.ToArray(), Models.TaskState.Executing);
                        tran.Commit();
                        return tasks;
                    }
                    catch (Exception)
                    {
                        tran.Rollback();
                        throw;
                    }
                }

            }
        }
        /// <summary>
        /// 创建任务
        /// </summary>
        /// <param name="typeFlag">任务类型标识</param>
        /// <param name="taskName">任务名称</param>
        /// <param name="datas">任务数据</param>
        /// <param name="presetTime">预约执行时间</param>
        /// <returns></returns>
        public TaskModel[] CreateTasks(string typeFlag, string taskName, string[] datas, DateTime? presetTime)
        {
            using (var dbt = this.db.Clone())
            {
                var dao = new TaskDAO(dbt);
                List<long> list = new List<long>();
                foreach (var data in datas)
                {
                    TaskModel task = new TaskModel { TypeFlag = typeFlag, Name = taskName, Data = data, PresetTime = presetTime };
                    var id = dao.Insert<TaskModel, long>(task);
                    if (id.HasValue)
                    {
                        list.Add(id.Value);
                    }

                }
                return dao.Select<TaskModel>(qcb => qcb.inlist("`Id`", list.ToArray()));
            }
        }

        public void Dispose()
        {
            timer.Dispose();
            lock (asyncState_threads)
            {
                threads.Clear();
            }
            executeMethod = null;
        }
        #endregion
    }
}